/*
* UIGen-Pro standard JavaScript library.
* Copyright © 2004, 2005 Ubicom Inc. . All rights reserved.
*/
//***************************************************************************
// Utility functions
/*
* getAttribute()
* Get the value of an attribute from a DOM node by name, or return
* null if the attribute doesn't exist. Thanks to the overly complex and
* generally sucky DOM standards, the proper way to do this is both
* browser dependent and node-type dependent. Try a bunch of different
* ways before giving up.
*/
function getAttribute (node,attribute_name)
{
/*
* First try a fast way that works in newer versions of MSIE but not
* in Netscape/Mozilla.
*/
if (node[attribute_name]) {
return node[attribute_name];
}
/*
* Hmm, that didn't work, so try a (hopefully) more portable way that
* should work with IE4+ and netscape. Of course it's quite possible
* that the attribute doesn't exist, but there's no harm in trying
* to access it a different way.
*/
if (node.getAttribute) {
return node.getAttribute (attribute_name);
}
/*
* Well that didn't work either. The node doesn't have the getAttribute()
* function, so try another semi-portable XMLDOM-ish way.
*/
if (node.attributes) {
var attr = node.attributes.getNamedItem (attribute_name);
if (attr) return attr.nodeValue;
}
/*
* Give up.
*/
return null;
}
/*
* getEventObject()
* This is called from event handling functions to find the object
* that invoked this event. This is horribly browser dependent,
* which is why we have a separate function to do it.
* The parameter 'e' is what is passed to the event handling function.
*/
function getEventObject (evt)
{
evt = (evt) ? evt : ((window.event) ? window.event : "");
if (evt) {
var obj = (evt.srcElement != null) ? evt.srcElement : evt.target;
} else {
var obj = null;
}
return obj;
}
/*
* cloneObject()
* Make a deep (i.e. recursive) cloned copy of the fields in the
* given Object. Note that a=cloneObject(b) is different from a=b,
* as the latter simply assigns a reference to the object to the
* variable, making both a and b refer to the same instance, whereas
* cloneObject() makes a separate instance.
*/
function cloneObject (obj)
{
var newobj = new Object
for (var i in obj) {
if (typeof(obj[i]) == "object") {
newobj[i] = cloneObject (obj[i]);
}
else {
newobj[i] = obj[i];
}
}
return newobj;
}
/*
* copyObject()
* Make a deep (i.e. recursive) cloned copy of the fields in the
* source Object, copying them to the destination object.
* The destination is presumed to already have the necessary
* object members.
*/
function copyObject (dest,src,field_name_to_ignore)
{
for (var i in src) {
if (typeof(src[i]) == "object") {
copyObject (dest[i],src[i]);
}
else {
if (i != field_name_to_ignore) {
dest[i] = src[i];
}
}
}
}
/*
* compareObject()
* Make a deep (i.e. recursive) comparison of the fields in the
* A and B objects. return 1 if the same or 0 if not.
* B object is presumed to have the same object members as A,
* however if B has any members not present in A they will be
* ignored.
*/
function compareObject (A,B,field_name_to_ignore)
{
for (var i in A) {
if (typeof(B[i]) == "undefined") continue;
if (typeof(A[i]) == "object") {
if (compareObject (A[i],B[i])==0) return 0;
}
else {
if (i != field_name_to_ignore && A[i] != B[i]) {
return 0;
}
}
}
return 1;
}
/*
* appendArray()
* Append array B to array A, but don't use concat() because that
* would create a separate array object, resulting in poor performance.
*/
function appendArray (A,B)
{
var k = A.length;
var n = B.length;
for (var i=0; i> 18) & 0x3f) +
b64.charAt((x >> 12) & 0x3f) +
b64.charAt((x >> 6) & 0x3f) +
b64.charAt(x & 0x3f);
}
return barray.join ("");
}
/*
* buildCharToSixBitsArray()
* Build a 256 entry array that convert a base-64 character
* to its corresponding 6-bit number. Invalid characters will
* be translated to 0.
*/
function buildCharToSixBitsArray()
{
b64_char_to_6 = new Array;
for (var i=0; i<256; i++) b64_char_to_6[i] = 0;
for (var i=0; i<26; i++) b64_char_to_6[i+65] = i+1;
for (var i=0; i<26; i++) b64_char_to_6[i+97] = i+27;
for (var i=0; i<10; i++) b64_char_to_6[i+48] = i+53;
b64_char_to_6[95] = 63;
}
buildCharToSixBitsArray();
/*
* convertFromBase64()
* Read base-64 encoded binary data from the string `buf', and return
* the resulting array of 8-bit numbers. Invalid characters in the
* string will be interpreted as '.'.
*/
function convertFromBase64 (buf)
{
while (buf.length & 3) buf += '.'; // make 'buf' a multiple of 4
var dst = new Array;
var j = 0;
for (var i = 0; i < buf.length; i += 4) {
var cc = (b64_char_to_6 [buf.charCodeAt(i)] << 18) |
(b64_char_to_6 [buf.charCodeAt(i+1)] << 12) |
(b64_char_to_6 [buf.charCodeAt(i+2)] << 6) |
(b64_char_to_6 [buf.charCodeAt(i+3)]);
dst[j] = (cc >> 16) & 0xff;
dst[j+1] = (cc >> 8) & 0xff;
dst[j+2] = (cc) & 0xff;
j += 3;
}
return dst;
}
//***************************************************************************
// Conversion functions: convert data between the string representation used
// by form elements and the binary array representation used to pass data to
// and from the server.
/*
* intToByteArray()
* Convert an integer value to a byte array of size 'length'.
*/
function intToByteArray (value,length)
{
var a = new Array;
for (var i=0; i>> ((length-1-i)*8)) & 0xff;
}
return a;
}
/*
* byteArrayToInt()
* Convert the byte array indexes start_index to end_index-1 to an integer.
* The number of bytes in the range must be is 1 to 4.
* If 4 bytes are converted then the returned value will be signed,
* otherwise the returned value will be unsigned for 1-3 bytes converted.
*/
function byteArrayToInt (b,start_index,end_index)
{
var n=0;
for (var i=start_index; i= length) break;
a[j] = c;
j += 1;
}
else if (c <= 0x7FF) {
if (j+1 >= length) break;
a[j] = (c >> 6) | 0xc0;
a[j+1] = (c & 0x3f) | 0x80;
j += 2;
}
else if (c <= 0xFFFF) {
if (j+2 >= length) break;
a[j] = (c >> 12) | 0xe0;
a[j+1] = ((c >> 6) & 0x3f) | 0x80;
a[j+2] = (c & 0x3f) | 0x80;
j += 3;
}
else if (c <= 0x10FFFF) {
if (j+3 >= length) break;
a[j+0] = (c >> 24) | 0xf0;
a[j+1] = ((c >> 12) & 0x3f) | 0x80;
a[j+2] = ((c >> 6) & 0x3f) | 0x80;
a[j+3] = (c & 0x3f) | 0x80;
j += 4;
}
else {
// Default behavior when we don't know what else to do
if (j >= length) break;
a[j] = c;
j += 1;
}
}
a[length] = 0;
return a;
}
/*
* byteArrayToString()
* Convert a byte array to a string. Only array indexes
* between start_index and end_index-1 are examined.
* UTF-8 byte sequences are converted properly.
*/
function byteArrayToString (b,start_index,end_index)
{
var s = "";
for (var i=start_index; i 255) return null;
x = x | (q << ((4-i)*8));
}
return x;
}
else return null;
}
/*
* IPAddressToByteArray()
* Convert an IP address formatted string into a 4 element byte array.
* Return 0 if the string is not formatted correctly.
*/
function IPAddressToByteArray (s)
{
s = new String(s);
var got = s.match (/^\s*(\d+)\s*[.]\s*(\d+)\s*[.]\s*(\d+)\s*[.]\s*(\d+)\s*$/);
if (got) {
var a = new Array;
for (var i=1; i <= 4; i++) {
var q = parseInt(got[i],10);
if (q < 0 || q > 255) return 0;
a[i-1] = q;
}
return a;
}
else return 0;
}
/*
* byteArrayToIPAddress()
* Convert a byte array 'a' into an IP address formatted string.
* Elements in 'a' from start_index to start_index+3 are used.
*/
function byteArrayToIPAddress (a,start_index)
{
var s = "";
for (var i=0; i < 4; i++) {
s += a[i+start_index].toString(10);
if (i < 3) s += ".";
}
return s;
}
/*
* hexStringToByteArray()
* Convert a hex string 's' into a byte array of 'length' bytes.
* If 's' is shorter than 'length' bytes, the result will be padded with zeros.
* Ignore leading and trailing whitespace. If there are any conversion errors
* or the string is invalid then return 0, else return a byte array.
* If the string is empty or all whitespace, return an array of all zeros.
* Ignore the commonly used separator characters (:,-)
*/
function hexStringToByteArray (s,length)
{
// Ignore the commonly used separator characters
s = new String(s);
s = s.replace (/[:-]/g,'');
// if the string is empty, return all zeros
if (s.match (/^\s*$/)) {
var a = new Array;
for (var i=0; i length*2) return 0; // error: string to long
if (s.length & 1) return 0; // error: an even number of characters needed
// pad string with zeros
while (s.length < length*2) {
s = s + '0';
}
var a = new Array;
for (var i=0; i < length; i++) {
a[i] = parseInt(s.substr(i*2,2),16);
}
return a;
}
else return 0;
}
/*
* byteArrayToHexString()
* Convert a byte array 'a' into a hex string. Only array indexes
* between start_index and end_index-1 are examined.
* If the 'separator' argument is given it will be used to separate the bytes.
*/
function byteArrayToHexString (a,start_index,end_index,separator)
{
var s = "";
if (typeof(separator) != "string") separator = "";
for (var i=start_index; i length) {
alert ('The string "' + value + '" is too long\n(maximum length is ' + length + ' characters).');
byte_array_has_error = 1;
return;
}
appendArray (byte_array,stringToByteArray (value,length));
}
/*
* sC()
* Single byte character.
*/
function sC()
{
var value = String.fromCharCode (byte_array[i]);
i++;
return value;
}
/*
* gS()
* Single byte character.
*/
function gC (value)
{
if (byte_array_has_error) return;
byte_array[byte_array.length] = value.charCodeAt (0);
}
/*
* sU()
* Unsigned integer of the given number of bytes (1 to 4).
*/
function sU (length)
{
var value = byteArrayToInt (byte_array,i,i+length);
i += length;
if (length == 4 && value < 0) {
// byteArrayToInt() returns a signed value if length==4, so compensate
value = value + 4294967296;
}
return value;
}
/*
* gIU_Helper()
* Helper function for gI() and gU()
*/
function gIU_Helper (value,length,signed)
{
if (byte_array_has_error) return;
var v;
if (value==true || value=="true") {
v = 1;
}
else if (value==false || value=="false") {
v = 0;
}
else {
v = convertNumber (value);
if (v == null) {
alert ('The number "' + value + '" is not valid.');
byte_array_has_error = 1;
return;
}
}
if (signed==0 && v < 0) {
alert ('The number "' + value + '" must be positive.');
byte_array_has_error = 1;
return;
}
// range checking
var min,max;
if (length <= 3) {
if (signed==0) {
max = (1<<(length*8))-1;
min = 0;
}
else {
max = (1<<(length*8-1))-1;
min = -max-1;
}
}
else {
if (signed==0) {
max = 4294967295;
min = 0;
}
else {
max = 2147483647;
min = -max-1;
}
}
if (v < min || v > max) {
alert ('The number "' + value + '" is not in the correct range.');
byte_array_has_error = 1;
return;
}
appendArray (byte_array,intToByteArray (v,length));
}
/*
* gU()
* Unsigned integer of the given number of bytes (1 to 4).
*/
function gU (value,length)
{
gIU_Helper (value,length,0);
}
/*
* sI()
* Signed integer of the given number of bytes (1 to 4).
*/
function sI (length)
{
var value = byteArrayToInt (byte_array,i,i+length);
i += length;
var topbit = 1 << (length*8-1);
if (length < 4 && (value & topbit) != 0) {
// do a sign conversion within the lower 'length' bytes.
if (value == topbit) {
value = -topbit;
}
else {
value = -( ((~value)+1) & (topbit-1) );
}
}
return value;
}
/*
* gI()
* Signed integer of the given number of bytes (1 to 4).
*/
function gI (value,length)
{
gIU_Helper (value,length,1);
}
/*
* sH()
* Hex string of the given length (in bytes).
*/
function sH (length,separator)
{
if (length==6 && separator==null) {
separator = ':'; // hack to get MAC addresses to display with colons
}
var value = byteArrayToHexString (byte_array,i,i+length,separator);
i += length;
return value;
}
/*
* gH()
* Hex string of the given length (in bytes).
*/
function gH (value,length)
{
if (byte_array_has_error) return;
var h = hexStringToByteArray (value,length);
if (h==0) {
alert ('The hex string "' + value + '" is not valid.')
byte_array_has_error = 1;
return;
}
appendArray (byte_array,h);
}
/*
* sX()
* IP address.
*/
function sX()
{
var value = byteArrayToIPAddress (byte_array,i);
i += 4;
return value;
}
/*
* gX()
* IP address.
*/
function gX (value)
{
if (byte_array_has_error) return;
var ip = IPAddressToByteArray (value);
if (ip==0) {
alert ('The IP address "' + value + '" is not valid.')
byte_array_has_error = 1;
return;
}
appendArray (byte_array,ip);
}
//***************************************************************************
// Helper functions for various user interface elements
function doExpandCollapse (e)
{
var obj = getEventObject (e);
var s = getAttribute (obj,'_ALT_VALUE');
obj.setAttribute ('_ALT_VALUE',obj.value);
obj.value = s;
var v = getAttribute (obj,'_TOGGLE_VARIABLE');
eval (v + ' = !' + v);
dataChanged();
}
//***************************************************************************
// Array editor class
/*
* lookForUnusedEntry()
* Look for the first unused entry in an array given an arrayEditor object.
* Return -1 if all entries are used.
*/
function lookForUnusedEntry (ae)
{
var i;
if (ae.is_stack == 1) {
for (i=ae.max_elements-1; i>=0; i--) {
if (ae.thearray[i].used==0) return i;
}
}
else
{
for (i=0; i= 0 ? this.sel : lookForUnusedEntry (this);
if (i < 0) {
alert ('There is no room for any more entries.');
return 0;
}
/*
* Check for blank or duplicate primary keys
*/
if (typeof(this.primary_key)=="string") {
if (this.thearray[-1][this.primary_key] == "") {
alert ("The '"+this.primary_key_name+"' field can not be blank");
return 0;
}
this.thearray[-1][this.primary_key] = trimString(this.thearray[-1][this.primary_key]);
for (var j=0; j 0) {
if (!confirm ("You have unsaved changes in the entry you are editing.\n"+
"Press 'Ok' to abandon these changes and perform the requested action.\n"+
"Otherwise press 'Cancel'.")) {
return 0;
}
}
return 1;
}
/*
* arrayEditor:checkIfEditing()
* Return 1 if there is changed-but-unsaved data in the array itself.
* Return 2 if there are new edits not yet saved in the array.
* Otherwise return 0.
*/
function arrayEditor_checkIfEditing (field_to_ignore)
{
if (this.sel >= 0) {
if (compareObject (this.thearray[-1],this.thearray[this.sel],field_to_ignore) == 0) {
return 1;
}
}
else {
/*
* Prepare element [-2] to be a cleaned clone of a proper element.
*/
this.thearray[-2] = cloneObject (this.thearray[0]);
if (this.clear_entry_function_valid) {
this.clear_entry_function(-2);
}
/*
* Compare the clean element against editing element (-1) to see if the user has
* made changes.
*/
if (compareObject (this.thearray[-1],this.thearray[-2]) == 0) {
return 2;
}
}
return 0;
}
function arrayEditor_cancelEditingEntry()
{
if (this.clear_entry_function_valid) {
this.clear_entry_function (-1);
}
this.sel = -1;
this.is_under_edit = 0;
dataChanged();
}
function generateArray (ae, has_enabled_column, has_edit_delete_column, elements)
{
for (var i=0; i';
if (has_enabled_column) {
html += '';
}
for (var j=0; j" eval="" (elements[j].replace="" (="" \@index\@="" g,i))="" '<="" td="">';
}
if (has_edit_delete_column) {
html += '
';
html += '
';
}
html += '';
document.write (html);
}
}
/*
* anyPageArrayEditorsChanged()
* Return 1 if any of the non- information-only array editors on the page
* contain changed data and the user does not want to abandon it.
* Otherwise make sure all the information is saved and return 0 if it
* was saved successfully or 1 if not.
*/
function anyPageArrayEditorsChanged()
{
if (typeof(page_array_editors) != 'undefined') {
for (var i=0; i= 0) {
fromform = element.options [element.selectedIndex].value;
}
}
break;
}
if (data_to_form) {
if (toform != '') eval (toform);
}
else {
if (fromform != null && getAttribute (element,'_READONLY')==null) {
eval (name + '=fromform');
}
}
}
}
}
if (!data_to_form) {
naturalizeDataObject();
}
}
//***************************************************************************
// Form submission
function doSave()
{
if (anyPageArrayEditorsChanged()) {
return 0;
}
formChanged (null);
if (typeof(pageVerify) == "function") {
if (!pageVerify()) return;
}
byte_array_has_error = 0;
createBinaryArrayFromDataObject();
if (byte_array_has_error) return;
/*
* Post the new configuration to the server
*/
document.postform.data.value = convertToBase64 (byte_array);
document.postform.submit();
}
function doCancel()
{
if (confirm ("Do you want to abandon all changes you made to this page?")) {
if (typeof(onCancelPage) == "function") {
onCancelPage();
}
uigenInitAtStart (false);
if (typeof(onCancelComplete) == "function") {
onCancelComplete();
}
dataChanged();
}
}
function restoreError (message)
{
alert ("The restoration of settings failed ("+message+")\nPress OK to continue");
restore_error = 1;
history.go(-1);
return 0;
}
function showRestoreMessage (i)
{
eval ('restore_phase_done_' + i + ' = 1');
executeActiveTags();
}
function doRestore (phase)
{
if (phase==1) {
restore_error = 0;
}
else {
if (restore_error) return;
}
if (phase==1) {
/*
* Convert the data string ('data' comes from the server)
*/
restore_error = 0;
byte_array = convertFromBase64 (data);
showRestoreMessage (1);
}
else if (phase==2) {
/*
* Create the local data object (unpackAll() comes from the server).
* We set all data to the existing values so that (1) values not specified in the
* saved file will not be changed, and (2) there will be no undefined values
* when we repack.
*/
data = null;
unpackAll();
showRestoreMessage (2);
/*
* Reset variables that will be loaded from the saved settings file.
*/
saved_data_object = data;
data = null;
unpackAll = null;
}
else if (phase==3) {
/*
* Convert the data string ('data' comes from the saved settings file)
*/
if (typeof(data) != "string") {
return restoreError ("bad settings file");
}
byte_array = convertFromBase64 (data);
showRestoreMessage (3);
}
else if (phase==4) {
/*
* Unpack the saved settings into the data object (unpackAll() comes from the settings file)
*/
data = saved_data_object;
if (typeof(unpackAll) != "function") {
return restoreError ("Bad settings file");
}
unpackAll();
showRestoreMessage (4);
}
else if (phase==5) {
/*
* Repack the data object into byte_array (packAll() comes from the server)
*/
byte_array_has_error = 0;
packAll();
if (byte_array_has_error) {
return restoreError ("Restored data not acceptable");
}
showRestoreMessage (5);
}
else if (phase==6) {
/*
* Post the new configuration to the server
*/
document.postform.data.value = convertToBase64 (byte_array);
showRestoreMessage (6);
document.postform.submit();
}
}
//***************************************************************************
// Send data to the server and get returned status information without needing
// a page load. This is done be loading an IFRAME block.
// Data may be encoded in the URL thusly: "foo.cgi?data=...."
var iframeCallback;
var iframeDoneMessage;
var iframeNumber = 0;
var iframeCheckCount = 0;
function iframeStandardCallback (iframe)
{
alert (iframeDoneMessage);
}
function checkIframeLoad()
{
iframeCheckCount++;
if (iframeCheckCount > 60) {
alert ("The action can not complete because the network connection seems to be down");
location.reload (true);
return;
}
if (typeof(iframeCallback) == "function") {
try {
var doc = eval('window.frames.myframe_'+iframeNumber+'.window.document');
var data = getDataFromXML (doc);
if (typeof(data)=="string" && data.length > 0) {
iframeCallback (doc);
return;
}
}
catch (err) {
}
window.setTimeout ('checkIframeLoad();',100);
}
}
/*
* sendDataToServer()
* Load an arbitrary URL from the server. Call the callback
* function when the returned data is available (or if it is a string, display that
* message when done).
*/
function sendDataToServer (url,callback_or_string)
{
if (typeof(callback_or_string) != "function") {
if (typeof(callback_or_string) == "string") iframeDoneMessage = callback_or_string; else iframeDoneMessage = "Done";
callback_or_string = iframeStandardCallback;
}
iframeCallback = callback_or_string;
var ifr = document.createElement ('DIV');
ifr.style.visibility = 'hidden';
ifr.style.position = 'absolute';
ifr.style.top = '0px';
ifr.style.left = '0px';
iframeNumber++;
ifr.innerHTML = '